using System;
using System.Collections.Generic;
using System.Linq;
using Godot;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Box;

public enum PropertyType {
    Number,
    Boolean,
    String,
    Code,
    Task,
    Action,
}

public partial class ProcessNodeControl : GraphNode {
    public string NodeType;

    public class Slot {
        public int ID;
        public ProcessNodeControlPort Left;
        public ProcessNodeControlPort Right;
    }

    public Dictionary<string,IProperty> Properties = new Dictionary<string, IProperty>();
    public List<Node> PropertyControls = new List<Node>();
    public Dictionary<string,ProcessNodeControlPort> InputPorts = new Dictionary<string, ProcessNodeControlPort>();
    public Dictionary<string,ProcessNodeControlPort> OutputPorts = new Dictionary<string, ProcessNodeControlPort>();
    public List<Slot> Slots = new List<Slot>();

    PackedScene ProcessTreeNodeSlotControlPackedScene;
    public string ProcessTreeNodeClassName;

    StyleBoxFlat FrameStyle;
    StyleBoxFlat SelectedFrameStyle;

    public LineEdit NameEdit = new LineEdit();
    public ProcessNodeControl() {
        ProcessTreeNodeSlotControlPackedScene = GD.Load<PackedScene>("res://source/process_tree_editor/node/ProcessTreeNodeSlotControl.tscn");

        NameEdit.PlaceholderText = "name...";
        AddChild(NameEdit);

        SetSlotEnabledLeft(0,true);
        SetSlotTypeLeft(0,-1);
        SetSlotColorLeft(0,new Color(0,0,0,0));
        SetSlotEnabledRight(0,true);
        SetSlotTypeRight(0,-1);
        SetSlotColorRight(0,new Color(0,0,0,0));

        DefineProperty("Label",PropertyType.String);
    }

    

    public struct _SettingPropertyItem {
        public string type;
        public string value;
    }

    public class _SettingEditorPortItem {
        public string name;
        public string type;
    }

    public class _SettingNodeStyle {
        public string top;
        public string buttom;
    }

    public struct _SettingNodeSetting {
        public _SettingEditorPortItem[] ports;
        public _SettingNodeStyle styles;
    }

    public struct _Setting {
        public string title;
        public string type;
        public Dictionary<string,_SettingPropertyItem> properties;
        public _SettingNodeSetting node_setting;
    }

    public void AllocSlot(_SettingEditorPortItem[] ports) {
        List<(_SettingEditorPortItem L,_SettingEditorPortItem R)> slots = new List<(_SettingEditorPortItem L, _SettingEditorPortItem R)>();

        // int si1 = 0;
        for(int i = 0;i< ports.Length;i++) {
            var port =  ports[i];
            if(port.type == "input") {
                slots.Add((port,null));
            }
        }

        int n = 0;
        for(int i = 0;i< ports.Length;i++) {
            var port =  ports[i];
            if(port.type == "output") {
                if(n >= slots.Count) {
                    slots.Add((null,port));
                } else {
                    var slot = slots[n];
                    slot.R = port;
                    slots[n] = slot;
                    n++;
                }
            }
        }

        foreach(var slot in slots) {
            PushSlot(slot.L != null ? slot.L.name : null,slot.R != null ? slot.R.name : null);
        }
    }

    public void Setting(string setting_file_path) {
        FileAccess setting_file = FileAccess.Open(setting_file_path,FileAccess.ModeFlags.Read);
        if(setting_file.IsOpen()) {
            try {
                _Setting setting =JsonConvert.DeserializeObject<_Setting>(setting_file.GetAsText());
                Title = setting.title;
                NodeType = setting.type;
                foreach(var property in setting.properties) {
                    string name = property.Key;
                    string type = property.Value.type;
                    string value = property.Value.value;
                    DefineProperty(name,PropertyHelper.StringTypeToPropertyType(type));
                }
                AllocSlot(setting.node_setting.ports);
                DefineStyle(new Godot.Color(setting.node_setting.styles.top),new Godot.Color(setting.node_setting.styles.buttom));

            } catch(Exception){} 
        }
    }

    public void DefineStyle(Color top_color,Color button_color) {
        FrameStyle = GD.Load("res://source/process_tree_editor/node/ProcessTreeNodeStylebox.stylebox").Duplicate() as StyleBoxFlat;
        FrameStyle.BgColor = button_color;
        FrameStyle.BorderColor = top_color;

        SelectedFrameStyle = GD.Load("res://source/process_tree_editor/node/ProcessTreeNodeStylebox.stylebox").Duplicate() as StyleBoxFlat;
        SelectedFrameStyle.BgColor = button_color * 0.8f;
        SelectedFrameStyle.BorderColor = top_color * 0.8f;

        AddThemeColorOverride("title_color",button_color);
        AddThemeStyleboxOverride("frame",FrameStyle);
        AddThemeStyleboxOverride("selected_frame",SelectedFrameStyle);
    }

    

    public ProcessNodeControl DefineProperty(string name,PropertyType type) {
        Node node = PropertyHelper.CreatePropertyControl(type,name);
        PropertyControls.Add(node);
        Properties[name] = node as IProperty;
        return this;
    }

    #nullable enable
    public ProcessNodeControl PushSlot(string? left_name,string? right_name) {
        ProcessTreeNodeSlotControl slot_control = ProcessTreeNodeSlotControlPackedScene.Instantiate<ProcessTreeNodeSlotControl>();
        AddChild(slot_control);
        Slot slot = new Slot();
        Slots.Add(slot);
        int slot_id = Slots.Count;
        slot.ID = slot_id;

        if(left_name != null) {
            slot_control.LeftLabel = left_name;
            ProcessNodeControlPort port = new ProcessNodeControlPort(left_name.ToString(),slot_id);
            port.Node = this;
            InputPorts[left_name] = port;
            slot.Left = port;
            SetSlotEnabledLeft(slot_id,true);
        } else {
            SetSlotEnabledLeft(slot_id,true);
            SetSlotTypeLeft(slot_id,-1);
            SetSlotColorLeft(slot_id,new Color(0,0,0,0));
        }

        if(right_name != null) {
            slot_control.RightLabel = right_name;
            ProcessNodeControlPort port = new ProcessNodeControlPort(right_name.ToString(),slot_id);
            port.Node = this;
            OutputPorts[right_name] = port;
            slot.Right = port;
            SetSlotEnabledRight(slot_id,true);
        } else {
            SetSlotEnabledRight(slot_id,true);
            SetSlotTypeRight(slot_id,-1);
            SetSlotColorRight(slot_id,new Color(0,0,0,0));
        }

        return this;

    }
    #nullable disable



    public void onConnectionRequest(ProcessNodeControlPort self_port,ProcessNodeControlPort to_port) {
        self_port.Connect(to_port);
    }

    public void onDisconnectionRequest(ProcessNodeControlPort self_port,ProcessNodeControlPort to_port) {
        if(to_port == null || self_port == null) return;
        self_port.Disconnect(to_port);
    }

    public virtual string GenerateInitCodeString(string var_name) {
        string code = "";
        foreach(Node _property in PropertyControls) {
            IProperty property = _property as IProperty;
            if(property.PName != "Label") {
                string property_str = property.ToString();
                if(property is StringPropertyControl) {
                    if(property_str == "") {
                        property_str = "\"\"";
                    } else {
                        property_str = $"\"{property_str}\"";
                    }
                }
                string c = $" {var_name}.{property.PName} = {property_str};";
                code += c;
            }
        }

        return code;
    }
}